package service

import (
	"crypto/rand"
	"fmt"
	"github.com/gin-gonic/gin"
	"os"
	"path/filepath"
	v1 "server_go/api/v1"
	"server_go/internal/model"
	"server_go/internal/repository"
	"strings"
	"time"
)

type FileService interface {
	UploadFile(ctx *gin.Context, req *v1.UploadFileRequest) (*v1.UploadFileResponse, error)
	DeleteFile(req *v1.DeleteFileRequest) (*v1.DeleteFileResponse, error)
	GetFileInfo(req *v1.GetFileInfoRequest) (*v1.GetFileInfoResponse, error)
	UploadAvatar(ctx *gin.Context, req *v1.UploadAvatarReq) (*v1.UploadFileResponse, error)
}

func NewFileService(
	service *Service,
	fileRepository repository.FileRepository,
	userRepo repository.UserRepository,
) FileService {
	return &fileService{
		Service:        service,
		fileRepository: fileRepository,
		userRepo:       userRepo,
	}
}

type fileService struct {
	*Service
	fileRepository repository.FileRepository
	userRepo       repository.UserRepository
}

func (s *fileService) UploadFile(ctx *gin.Context, req *v1.UploadFileRequest) (*v1.UploadFileResponse, error) {
	src, err := req.File.Open()
	if err != nil {
		return nil, v1.ErrBadGateway
	}

	defer src.Close()

	now := time.Now()
	year, month, day := now.Format("2006"), now.Format("01"), now.Format("02")
	targetDir := filepath.Join("static", year, month, day)
	if err = os.MkdirAll(targetDir, os.ModePerm); err != nil {
		return nil, err
	}

	uuid := generateUUID()                                  // 生成uuid
	ext := strings.ToLower(filepath.Ext(req.File.Filename)) // 取出文件扩展名

	filePath := filepath.Join(targetDir, uuid+ext)

	if err := ctx.SaveUploadedFile(req.File, filePath); err != nil {
		return nil, v1.ErrFileUploadFail
	}

	destPath := strings.ReplaceAll(filePath, `\`, `/`)

	fileId, err := s.fileRepository.UploadFile(&model.File{
		Name:     req.File.Filename,
		FilePath: destPath,
		FileType: ext,
	})
	if err != nil {
		return nil, v1.ErrFileUploadFail
	}

	return &v1.UploadFileResponse{
		FileId:   fileId,
		FilePath: destPath,
		FileName: uuid + ext,
	}, nil
}

func (s *fileService) DeleteFile(req *v1.DeleteFileRequest) (*v1.DeleteFileResponse, error) {
	if _, err := s.fileRepository.GetFileById(req.FileId); err != nil {
		return nil, err
	}
	if err := s.fileRepository.DeleteFile(req.FileId); err != nil {
		return nil, err
	}
	return &v1.DeleteFileResponse{FileId: req.FileId}, nil
}

func (s *fileService) GetFileInfo(req *v1.GetFileInfoRequest) (*v1.GetFileInfoResponse, error) {
	file, err := s.fileRepository.GetFileById(req.FileId)
	if err != nil {
		return nil, err
	}
	return &v1.GetFileInfoResponse{
		FileId:    int64(file.ID),
		FileName:  file.Name,
		FilePath:  file.FilePath,
		FileType:  file.FileType,
		CreatedAt: file.CreatedAt,
		UpdatedAt: file.UpdatedAt,
	}, nil
}

func (s *fileService) UploadAvatar(ctx *gin.Context, req *v1.UploadAvatarReq) (*v1.UploadFileResponse, error) {
	now := time.Now()
	year, month, day := now.Format("2006"), now.Format("01"), now.Format("02")
	targetDir := filepath.Join("static", year, month, day)
	if err := os.MkdirAll(targetDir, os.ModePerm); err != nil {
		return nil, err
	}

	uuid := generateUUID()                                  // 生成uuid
	ext := strings.ToLower(filepath.Ext(req.File.Filename)) // 取出文件扩展名

	filePath := filepath.Join(targetDir, uuid+ext)

	if err := ctx.SaveUploadedFile(req.File, filePath); err != nil {
		return nil, v1.ErrFileUploadFail
	}

	destPath := strings.ReplaceAll(filePath, `\`, `/`)

	fileId, err := s.fileRepository.UploadFile(&model.File{
		Name:     req.File.Filename,
		FilePath: destPath,
		FileType: ext,
	})
	if err != nil {
		return nil, v1.ErrFileUploadFail
	}
	if err := s.userRepo.UpdateUser(req.UserId, &model.User{AvatarId: fileId}); err != nil {
		return nil, err
	}
	return &v1.UploadFileResponse{
		FileId:   fileId,
		FilePath: destPath,
		FileName: uuid + ext,
	}, nil
}

func generateUUID() string {
	b := make([]byte, 16)
	_, _ = rand.Read(b)
	return fmt.Sprintf("%x", b)
}
